#!/bin/sh
#-
# Copyright (c) 2013 iXsystems, Inc., All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL Jordan Hubbard OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

. /etc/rc.subr

#
#	Active Directory Functions
#
: ${AD_CONFIG_PATH:="/etc/directoryservice/ActiveDirectory"}
: ${AD_CONFIG_FILE:="${AD_CONFIG_PATH}/config"}
: ${AD_CONTROL_FILE:="${AD_CONFIG_PATH}/ctl"}
: ${AD_NET_ADS_TIMEOUT:="10"}
: ${AD_DNS_TIMEOUT:="10"}
: ${AD_TOOL:="/usr/local/bin/adtool"}

__AD_proto()
{
	local proto="${1}"
	local default="${2}"

	if [ -z "${default}" ]
	then
		default="tcp"
	fi

	if [ -z "${proto}" ]
	then
		proto="${default}"
	fi

	proto="$(echo ${proto}|tr A-Z a-z)"
	if [ "${proto}" != "tcp" -a "${proto}" != "udp" ]
	then
		proto="${default}"
	fi

	echo "${proto}"
}

__AD_get_SRV_records()
{
	local host="${1}"
	local proto=$(__AD_proto "${2}" tcp)
	
	AD_log "__AD_get_SRV_records: host=${host}"

	if [ -z "${host}" ]
	then
		return 1
	fi

	AD_log "__AD_get_SRV_records: dig -t srv +short +nocomments ${host}"
	dig -t srv +short +nocomments "${host}" | egrep -v '^[[:space:]]*;;' | sort -n +0 +1

	return $?
}

__AD_get_SRV_host()
{
	local tmpfile="${1}"
	local proto=$(__AD_proto "${2}" tcp)

	if [ -z "${tmpfile}" -o ! -s "${tmpfile}" ]
	then
		return 1
	fi

	exec 3<&0
	exec 0<"${tmpfile}"
	while read -r line
	do
		local host=$(echo "${line}"|awk '{ print $4 }'|sed 's/\.$//')
		local port=$(echo "${line}"|awk '{ print $3 }')
		local nc_timeout="$(AD_get ad_dns_timeout)"
		local nc_args=

		AD_log "__AD_get_SRV_host: trying ${host}:${port}"

		if [ "${proto}" = "tcp" ]
		then
			nc_args="-z -d -w ${nc_timeout}"

		elif [ "${proto}" = "udp" ]
		then
			nc_args="-z -d -w ${nc_timeout} -u"
		fi
		
		#
		# UDP will always return that it was able to connect.
		# Is there a better way to do this?
		#
		/usr/bin/nc ${nc_args} "${host}" "${port}" >/dev/null 2>&1
		if [ "$?" = "0" ]
		then
			exec 0<&3
			echo "${host}:${port}"

			AD_log "__AD_get_SRV_host: Okay"
			return 0
		fi

		AD_log "__AD_get_SRV_host: Fail"
	done
	exec 0<&3

	return 1
}

__AD_tc()
{
	local timeout=$1
	shift
	local args="$*"

	local i=0
	local ret=1
	local lf="$(mktemp /tmp/tmp.XXXXXX)"

	lockf -k -s -t 0 ${lf} ${args} &
	ret=$?
	lpid=$!

	if [ "${ret}" != "0" ]
	then
		return ${ret}
	fi

	ret=2
	while [ ${i} -lt ${timeout} ]
	do
		sleep 1

		if lockf -k -s -t 0 ${lf} /usr/bin/true
		then
			rm -f ${lf}
			ret=0
			break
		fi

		: $((i += 1))
	done

	kill $! >/dev/null 2>&1
	pid="$(ps -axwww|grep "$args"|grep -v grep|awk '{ print $1 }')"
	if [ -n "${pid}" ]
	then
		kill ${pid} >/dev/null 2>&1
	fi

	pid="$(ps -axwww|grep "$args"|grep -v grep|awk '{ print $1 }')"
	if [ -n "${pid}" ]
	then
		kill -9 ${pid} >/dev/null 2>&1
	fi

	wait ${lpid}
	ret=$?

	rm -f ${lf}
	return ${ret}
}

__AD_get_SRV_record()
{
	local host="${1}"
	local proto=$(__AD_proto "${2}" tcp)
	local res

	local tmpfile="$(mktemp -q /var/tmp/.adfooXXXXXX)"

	if [ -z "${proto}" ]
	then
		
		proto="tcp"
	fi

	AD_log "__AD_get_SRV_record: host=${host}"
	__AD_get_SRV_records "${host}" > "${tmpfile}"

	__AD_get_SRV_host "${tmpfile}"
	res=$?

	rm "${tmpfile}"
	return ${res}
}

AD_get_root_domain()
{
	local domain="${1}"
	local rdnc="$(AD_query_rootDSE|egrep '^rootDomainNamingContext'|cut -f2- -d:|xargs)"
	local dnsroot="$(AD_get_partition "${rdnc}" dnsRoot|egrep '^dnsRoot'|cut -f2- -d:|xargs)"

	echo "${dnsroot}"
}

AD_locate_domain_controllers_by_guid()
{
	local domain="${1}"
	local guid="${2}"
	local record

	AD_log "AD_locate_domain_controllers_by_guid: domain=${domain}, guid=${guid}"

	if [ -z "${domain}" -o -z "${guid}" ]
	then
		return 1
	fi	

	record="_ldap._tcp.${guid}.domains._msdcs.${domain}"

	AD_log "AD_locate_domain_controllers_by_guid: record=${record}"
	__AD_get_SRV_records "${record}"

	return $?
}

AD_get_domain_controller_by_guid()
{
	local domain="${1}"
	local guid="${2}"
	local record
	local res

	local tmpfile="$(mktemp -q /var/tmp/.adfooXXXXXX)"

	AD_locate_domain_controllers_by_guid "${domain}" "${guid}" > "${tmpfile}"
	__AD_get_SRV_host "${tmpfile}"
	res=$?

	rm "${tmpfile}"
	return ${res}
}

AD_locate_domain_controllers()
{
	local domain="${1}"
	local site="${2}"
	local record

	AD_log "AD_locate_domain_controllers: domain=${domain}, site=${site}"

	if [ -z "${domain}" ]
	then
		return 1
	fi

	if [ -z "${site}" ]
	then
		record="_ldap._tcp.dc._msdcs.${domain}"
	else
		record="_ldap._tcp.${site}._sites.dc._msdcs.${domain}"
	fi

	AD_log "AD_locate_domain_controllers: record=${record}"
	__AD_get_SRV_records "${record}"

	return $?
}

AD_get_domain_controller()
{
	local domain="${1}"
	local site="${2}"
	local record
	local res

	local tmpfile="$(mktemp -q /var/tmp/.adfooXXXXXX)"

	AD_locate_domain_controllers "${domain}" "${site}" > "${tmpfile}"
	__AD_get_SRV_host "${tmpfile}"
	res=$?

	rm "${tmpfile}"
	return $?
}

AD_locate_primary_domain_controllers()
{
	local domain="${1}"
	local record

	AD_log "AD_locate_primary_domain_controllers: domain=${domain}"

	if [ -z "${domain}" ]
	then
		return 1
	fi

	record="_ldap._tcp.pdc._msdcs.${domain}"

	AD_log "AD_locate_primary_domain_controllers: record=${record}"
	__AD_get_SRV_records "${record}"

	return $?
}

AD_get_primary_domain_controller()
{
	local domain="${1}"
	local res

	local tmpfile="$(mktemp -q /var/tmp/.adfooXXXXXX)"

	AD_locate_primary_domain_controllers "${domain}" > "${tmpfile}"
	__AD_get_SRV_host "${tmpfile}"
	res=$?

	rm "${tmpfile}"
	return ${res}
}

AD_locate_ldap_servers()
{
	local domain="${1}"
	local site="${2}"
	local record

	AD_log "AD_locate_ldap_servers: domain=${domain}, site=${site}"

	if [ -z "${domain}" ]
	then
		return 1
	fi

	if [ -z "${site}" ]
	then
		record="_ldap._tcp.${domain}"
	else
		record="_ldap._tcp.${site}._sites.${domain}"
	fi

	AD_log "AD_locate_ldap_servers: record=${record}"
	__AD_get_SRV_records "${record}"

	return $?
}

AD_get_ldap_server()
{
	local domain="${1}"
	local site="${2}"
	local record
	local res

	local tmpfile="$(mktemp -q /var/tmp/.adfooXXXXXX)"

	AD_locate_ldap_servers "${domain}" "${site}" > "${tmpfile}"
	__AD_get_SRV_host "${tmpfile}"
	res=$?

	rm "${tmpfile}"
	return ${res}
}

AD_locate_forest_global_catalog_servers()
{
	local forest="${1}"
	local site="${2}"
	local record

	AD_log "AD_locate_forest_global_catalog_servers: forest=${forest}, site=${site}"

	if [ -z "${forest}" ]
	then
		return 1
	fi

	if [ -z "${site}" ]
	then
		record="_ldap._tcp.gc._msdcs.${forest}"
	else
		record="_ldap._tcp.${site}._sites.gc._msdcs.${forest}"
	fi

	AD_log "AD_locate_forest_global_catalog_servers: record=${record}"
	__AD_get_SRV_records "${record}"

	return $?
}

AD_get_forest_global_catalog_server()
{
	local forest="${1}"
	local site="${2}"
	local res

	local tmpfile="$(mktemp -q /var/tmp/.adfooXXXXXX)"

	AD_locate_forest_global_catalog_servers "${forest}" "${site}" > "${tmpfile}"
	__AD_get_SRV_host "${tmpfile}"
	res=$?

	rm "${tmpfile}"
	return ${res}
}

AD_locate_domain_global_catalog_servers()
{
	local domain="${1}"
	local site="${2}"

	AD_log "AD_locate_domain_global_catalog_servers: domain=${domain}, site=${site}"

	if [ -z "${domain}" ]
	then
		return 1
	fi

	if [ -z "${site}" ]
	then
		record="_gc._tcp.${domain}"
	else
		record="_gc._tcp.${site}._sites.${domain}"
	fi

	AD_log "AD_locate_global_catalogservers: record=${record}"
	__AD_get_SRV_records "${record}"

	return $?
}

AD_get_domain_global_catalog_server()
{
	local domain="${1}"
	local site="${2}"
	local res

	local tmpfile="$(mktemp -q /var/tmp/.adfooXXXXXX)"

	AD_locate_domain_global_catalog_servers "${domain}" "${site}" > "${tmpfile}"
	__AD_get_SRV_host "${tmpfile}"
	res=$?

	rm "${tmpfile}"
	return ${res}
}

AD_locate_kerberos_servers()
{
	local domain="${1}"
	local proto="${2}"
	local site="${3}"
	local record

	AD_log "AD_locate_kerberos_servers: domain=${domain}, proto=${proto}, site=${site}"

	if [ -z "${proto}" ]
	then
		proto="udp"
	fi

	proto="$(echo ${proto}|tr A-Z a-z)"
	if [ "${proto}" != "tcp" -a "${proto}" != "udp" ]
	then
		proto="udp"
	fi

	if [ -z "${site}" ]
	then
		record="_kerberos._${proto}.${domain}"
	else
		record="_kerberos._${proto}.${site}._sites.${domain}"
	fi

	AD_log "AD_locate_kerberos_servers: record=${record}"
	__AD_get_SRV_records "${record}"

	return $?
}

AD_get_kerberos_server()
{
	local domain="${1}"
	local proto="${2}"
	local site="${3}"
	local res

	local tmpfile="$(mktemp -q /var/tmp/.adfooXXXXXX)"

	AD_locate_kerberos_servers "${domain}" "${proto}" "${site}" > "${tmpfile}"
	__AD_get_SRV_host "${tmpfile}"
	res=$?

	rm "${tmpfile}"
	return ${res}
}

AD_locate_kpasswd_servers()
{
	local domain="${1}"
	local proto="${2}"
	local site="${3}"
	local record

	AD_log "AD_locate_kpasswd_servers: domain=${domain}, proto=${proto}, site=${site}"

	if [ -z "${proto}" ]
	then
		proto="udp"
	fi

	proto="$(echo ${proto}|tr A-Z a-z)"
	if [ "${proto}" != "tcp" -a "${proto}" != "udp" ]
	then
		proto="udp"
	fi

	if [ -z "${site}" ]
	then
		record="_kpasswd._${proto}.${domain}"
	else
		record="_kpasswd._${proto}.${site}._sites.${domain}"
	fi

	AD_log "AD_locate_kpasswd_servers: record=${record}"
	__AD_get_SRV_records "${record}"

	return $?
}

AD_get_kpasswd_server()
{
	local domain="${1}"
	local proto="${2}"
	local site="${3}"
	local res

	local tmpfile="$(mktemp -q /var/tmp/.adfooXXXXXX)"

	AD_locate_kpasswd_servers "${domain}" "${proto}" "${site}" > "${tmpfile}"
	__AD_get_SRV_host "${tmpfile}"
	res=$?

	rm "${tmpfile}"
	return ${res}
}

__do_AD_query()
{
	local dchost="${1}"
	local dcport="${2}"
	local basedn="${3}"
	local binddn="${4}"
	local bindpw="${5}"
	local scope="${6}"
	local filter="${7}"
	local attributes="${8}"
	local tmpfile="$(mktemp -q /var/tmp/.adbarXXXXXX)"

	: ${dchost:="$(AD_get ad_dchost)"}
	: ${dcport:="$(AD_get ad_dcport)"}
	: ${basedn:="$(AD_get ad_basedn)"}
	: ${binddn:="$(AD_get ad_binddn)"}
	: ${bindpw:="$(AD_get ad_bindpw)"}
	: ${scope:="sub"}
	: ${filter:="(objectclass=*)"}

	cat<<-__EOF__>"${tmpfile}"
	/usr/local/bin/ldapsearch \
	    -H "ldap://${dchost}:${dcport}" \
	    -b "${basedn}" \
	    -D "${binddn}" \
	    -w "${bindpw}" \
	    -s "${scope}" \
	    -x \
	    -z 0 \
	    -E 'pr=16384/noprompt' \
	    -LLL \
	    -l "${LDAP_TIMEOUT}" \
	    "${filter}" \
	    ${attributes}
__EOF__

	eval $(cat "${tmpfile}")
	rm "${tmpfile}"
}

AD_query()
{
	local basedn="${1}"
	local filter="${2}"

	shift
	local attributes="${*}"

	: ${basedn:="$(AD_get ad_basedn)"}
	: ${filter:='(objectclass=*)'}

	AD_log "AD_query: basedn = ${basedn}, filter = ${filter}, attributes = ${attributes}"

	local dchost="$(AD_get ad_dchost)"
	local dcport="$(AD_get ad_dcport)"
	local binddn="$(AD_get ad_binddn)"
	local bindpw="$(AD_get ad_bindpw)"

	__do_AD_query "${dchost}" "${dcport}" "${basedn}" \
		"${binddn}" "${bindpw}" "sub" "${filter}" \
		"${attributes}" 2>/dev/null
}

AD_get_partitions()
{
	local config="$(AD_query_rootDSE|egrep '^configurationNamingContext'|cut -f2- -d:|xargs)"
	local basedn="CN=Partitions,${config}"

	AD_log "AD_get_partitions: config = ${config}, basedn = ${basedn}"

	AD_query "${basedn}" "" netbiosname|grep '^nETBIOSName'|cut -f2- -d:|xargs
}

AD_get_workgroup()
{
	local tmpfile="$(mktemp -q /var/tmp/.adbarXXXXXX)"
	local config="$(AD_query_rootDSE|egrep '^configurationNamingContext'|cut -f2- -d:|xargs)"


	local dchost="$(AD_get ad_dchost)"
	local dcport="$(AD_get ad_dcport)"
	local binddn="$(AD_get ad_binddn)"
	local bindpw="$(AD_get ad_bindpw)"
	local basedn="$(AD_get basedn)"

	local filter="(&(objectcategory=crossref)(nCName=${basedn}))"
	AD_log "AD_get_workgroup: filter = ${filter}, attributes = ${attributes}"

	cat<<-__EOF__>"${tmpfile}"
	/usr/local/bin/ldapsearch \
	    -H "ldap://${dchost}:${dcport}" \
	    -D "${binddn}" \
	    -w "${bindpw}" \
	    -b "${config}" \
	    -s sub\
	    -x \
	    -z 0 \
	    -LLL \
	    -l "${LDAP_TIMEOUT}" \
	   "${filter}"
__EOF__

	eval $(cat "${tmpfile}")|grep '^nETBIOSName'|cut -f2- -d:|xargs
	rm "${tmpfile}"
}

AD_get_partition()
{
	local ncname="${1}"
	shift
	local attributes="${*}"

	local config="$(AD_query_rootDSE|egrep '^configurationNamingContext'|cut -f2- -d:|xargs)"
	local basedn="CN=Partitions,${config}"

	AD_log "AD_get_partition: config = ${config}, basedn = ${basedn}, ncname = ${ncname}"
	AD_query "${basedn}" "ncname=${ncname}" ${attributes}
}

AD_query_global_catalog()
{
	local filter="${1}"

	shift
	local attributes="${*}"
	local tmpfile="$(mktemp -q /var/tmp/.adbarXXXXXX)"

    	: ${filter:='(objectclass=*)'}

	AD_log "AD_query_global_catalog: filter = ${filter}, attributes = ${attributes}"

	local gchost="$(AD_get ad_gchost)"
	local gcport="$(AD_get ad_gcport)"
	local binddn="$(AD_get ad_binddn)"
	local bindpw="$(AD_get ad_bindpw)"

	cat<<-__EOF__>"${tmpfile}"
	/usr/local/bin/ldapsearch \
	    -H "ldap://${gchost}:${gcport}" \
	    -D "${binddn}" \
	    -w "${bindpw}" \
	    -b "" \
	    -s sub \
	    -x \
	    -z 0 \
	    -LLL \
	    -l "${LDAP_TIMEOUT}" \
	    "${filter}" \
	    ${attributes}
__EOF__

	eval $(cat "${tmpfile}")
	rm "${tmpfile}"
}

AD_query_rootDSE()
{
	local filter="${1}"
	local tmpfile="$(mktemp -q /var/tmp/.adbarXXXXXX)"

	: ${filter:='(objectclass=*)'}

	AD_log "AD_query_rootDSE: filter = ${filter}, attributes = ${attributes}"

	local dchost="$(AD_get ad_dchost)"
	local dcport="$(AD_get ad_dcport)"
	local binddn="$(AD_get ad_binddn)"
	local bindpw="$(AD_get ad_bindpw))"

	cat<<-__EOF__>"${tmpfile}"
	/usr/local/bin/ldapsearch \
	    -H "ldap://${dchost}:${dcport}" \
	    -D "${binddn}" \
	    -w "${bindpw}" \
	    -b "" \
	    -s base \
	    -x \
	    -z 0 \
	    -LLL \
	    -l "${LDAP_TIMEOUT}" \
	   "${filter}"
__EOF__

	eval $(cat "${tmpfile}")
	rm "${tmpfile}"
}


__do_AD_get()
{
	local prefix name var val

	prefix="${1}"
	name="${2}"
	var=\$$(printf "${prefix}${name}")
	val="$(eval "echo ${var} 2>/dev/null")"

	__unescape "${val}"
}

AD_get()
{
	local vars prefix

	vars="${*}"
	prefix="__ad_"

	for v in ${vars}
	do
		local var=\$$(printf "${v}")
		local val=$(eval "echo ${var} 2>/dev/null")

		if [ -n "${val}" ]
		then
			echo "${val}"
		else
			__do_AD_get "${prefix}" "${v}" 2>/dev/null
		fi
	done
}

__do_AD_set()
{
	local prefix var val pair

	prefix="${1}"
	var="${2}"
	val="$(__escape "${3}")"
	pair="${var}=${val}"

	eval "${prefix}${pair}"
}

AD_set()
{
	local var val prefix

	var="${1}"
	val="${2}"
	prefix="__ad_"

	__do_AD_set "${prefix}" "${var}" "${val}" 2>/dev/null
}

AD_save()
{
	local prefix="__save_"
	local vars="ad_bindname ad_domainname ad_basedn ad_binddn ad_dcname \
		ad_dchost ad_dcport ad_gcname ad_gchost ad_gcport ad_krbname \
		ad_krbhost ad_krbport ad_kpwdname ad_kpwdhost ad_kpwdport \
		ad_timeout ad_dns_timeout"
	
	for v in ${vars}
	do
		__do_AD_set "${prefix}" "${v}" "$(AD_get ${v})"
	done
}

AD_restore()
{
	local prefix="__save_"
	local vars="ad_bindname ad_domainname ad_basedn ad_binddn ad_dcname \
		ad_dchost ad_dcport ad_gcname ad_gchost ad_gcport ad_krbname \
		ad_krbhost ad_krbport ad_kpwdname ad_kpwdhost ad_kpwdport \
		ad_timeout ad_dns_timeout"
	
	for v in ${vars}
	do
		AD_set "${v}" "$(__do_AD_get ${prefix} ${v})"
	done
}

AD_show_config()
{
	printf "Active Directory Config:\n"
	printf "+------------------------------------------------+\n"
	printf "ad_domainname:         %s\n" $(AD_get ad_domainname)
	printf "ad_site:               %d\n" $(AD_get ad_site)
	printf "ad_dchost:             %s\n" $(AD_get ad_dchost)
	printf "ad_dcport:             %d\n" $(AD_get ad_dcport)
	printf "ad_gchost:             %s\n" $(AD_get ad_gchost)
	printf "ad_gcport:             %d\n" $(AD_get ad_gcport)
	printf "ad_krbhost:            %s\n" $(AD_get ad_krbhost)
	printf "ad_krbport:            %d\n" $(AD_get ad_krbport)
	printf "ad_kpwdhost:           %s\n" $(AD_get ad_kpwdhost)
	printf "ad_kpwdport:           %d\n" $(AD_get ad_kpwdport)
	printf "ad_basedn:             %s\n" $(AD_get ad_basedn)
	printf "ad_binddn:             %s\n" $(AD_get ad_binddn)
	printf "ad_bindpw:             %s\n" $(AD_get ad_bindpw)
	printf "ad_unix:               %s\n" $(AD_get ad_unix_extensions)
	printf "ad_trusted:            %s\n" $(AD_get ad_allow_trusted_doms)
	printf "ad_default:            %s\n" $(AD_get ad_use_default_domain)
	printf "ad_verbose:            %s\n" $(AD_get ad_verbose_logging)
	printf "ad_timeout:            %s\n" $(AD_get ad_timeout)
	printf "ad_dns_timeout:        %s\n" $(AD_get ad_dns_timeout)
	printf "ad_krb_realm:          %s\n" $(AD_get ad_krb_realm)
	printf "ad_keytab_principal:   %s\n" $(AD_get ad_keytab_principal)
	printf "ad_keytab_file:        %s\n" $(AD_get ad_keytab_file)
	printf "ad_ssl:                %s\n" $(AD_get ad_ssl)
	printf "+------------------------------------------------+\n"
}

AD_testjoin_domain()
{
	local bindname="${1}"
	local domainname="${2}"
	local res

	: ${bindname:="$(AD_get ad_bindname)"}
	: ${domainname:="$(AD_get ad_domainname)"}
	: ${dchost:="$(AD_get ad_dchost)"}
	: ${dcport:="$(AD_get ad_dcport)"}

	LOGNAME="${bindname}"
	export LOGNAME

	AD_log "AD_testjoin_domain: net -k ads testjoin ${domainname} -S ${dchost} -p ${dcport}"

	__AD_tc "$(AD_get ad_timeout)" /usr/local/bin/net -k ads testjoin "${domainname} -S ${dchost} -p ${dcport}"
	res=$?

	local ok="Failed"
	if [ "${res}" = "0" ]
	then
		ok="Successful"
	fi

	AD_log "AD_testjoin_domain: ${ok}"
	return ${res}
}

AD_join_domain()
{
	local bindname="${1}"
	local domainname="${2}"
	local res

	: ${bindname:="$(AD_get ad_bindname)"}
	: ${domainname:="$(AD_get ad_domainname)"}
	: ${dchost:="$(AD_get ad_dchost)"}
	: ${dcport:="$(AD_get ad_dcport)"}

	LOGNAME="${bindname}"
	export LOGNAME

	AD_log "AD_join_domain: net -k ads join ${domainname} -S ${dchost} -p ${dcport}"

	__AD_tc "$(AD_get ad_timeout)" /usr/local/bin/net -k ads join "${domainname} -S ${dchost} -p ${dcport}"
	res=$?

	local ok="Failed"
	if [ "${res}" = "0" ]
	then
		ok="Successful"
	fi

	AD_log "AD_join_domain: ${ok}"
	return ${res}
}

AD_enable_machine_account()
{
	${AD_TOOL} enable_machine_account
}

AD_status_domain()
{
	local bindname="${1}"
	local domainname="${2}"
	local res

	: ${bindname:="$(AD_get ad_bindname)"}
	: ${domainname:="$(AD_get ad_domainname)"}

	LOGNAME="${bindname}"
	export LOGNAME

	AD_log "AD_status_domain: net -k ads status ${domainname}"

	__AD_tc "$(AD_get ad_timeout)" /usr/local/bin/net -k ads status "${domainname}"
	res=$?

	local ok="Not okay"
	if [ "${res}" = "0" ]
	then
		ok="Okay"
	fi

	AD_log "AD_status_domain: ${ok}"
	return ${res}
}

AD_leave_domain()
{
	local bindname="${1}"
	local domainname="${2}"
	local res

	: ${bindname:="$(AD_get ad_bindname)"}
	: ${domainname:="$(AD_get ad_domainname)"}

	LOGNAME="${bindname}"
	export LOGNAME

	AD_log "AD_leave_domain: net -k ads leave ${domainname}"

	__AD_tc "$(AD_get ad_timeout)" /usr/local/bin/net -k ads leave "${domainname}"
	res=$?

	local ok="Failed"
	if [ "${res}" = "0" ]
	then
		ok="Successful"
	fi

	AD_log "AD_leave_domain: ${ok}"
	return ${res}
}

AD_disable_machine_account()
{
	${AD_TOOL} disable_machine_account
}

AD_has_unix_extensions()
{
	local ad_unix="$(AD_get ad_unix_extensions)"
	if [ "${ad_unix}" = "1" ]
	then
		return 0
	fi
	return 1
}

AD_has_keytab()
{
	local ad_keytab_principal="$(AD_get ad_keytab_principal)"
	local ad_keytab_file="$(AD_get ad_keytab_file)"

	if [ -n "${ad_keytab_principal}" -a -s "${ad_keytab_file}" ]
	then
		return 0
	fi
	return 1
}

AD_ssl_enabled()
{
	local ad_ssl="$(AD_get ad_ssl)"
	if [ "${ad_ssl}" = "on" ]
	then
		return 0
	fi
	return 1
}

AD_tls_enabled()
{
	local ad_ssl="$(AD_get ad_ssl)"
	if [ "${ad_ssl}" = "start_tls" ]
	then
		return 0
	fi
	return 1
}

AD_log()
{
	local args="$*"

	local on="$(AD_get ad_verbose_logging)"
	if [ "${on}" = "1" ]
	then
		logger -t "ActiveDirectory" ${args} >/dev/null 2>&1
	fi
}


AD_load_values()
{
	if ! AD_generate_config
	then
		return 1
	fi

	exec 3<&0
	exec 0<"${AD_CONFIG_FILE}"

	while read -r line
	do
		local var="$(echo ${line}|cut -f1 -d=|sed -Ee 's#^[[:space:]]+##' -e 's#[[:space:]]+$##')"
		local val="$(echo ${line}|cut -f2- -d=|sed -Ee 's#^[[:space:]]+##' -e 's#[[:space:]]+$##')"

		AD_set "${var}" "${val}"
	done
	exec 0<&3

	return 0
}

AD_generate_config()
{
	local res=0

	if [ ! -s "${AD_CONFIG_FILE}" ]
	then
		${AD_TOOL} get config_file > "${AD_CONFIG_FILE}"
		res=$?
		/bin/chmod 600 "${AD_CONFIG_FILE}"
	fi

	return ${res}
}

AD_remove_config()
{
	rm -f "${AD_CONFIG_FILE}"
}

AD_load_bindpw()
{
	local tmpfile="$(mktemp -q /var/tmp/.adfooXXXXXX)"

	cat << __SQL__ | ${FREENAS_SQLITE_CMD} -line ${FREENAS_CONFIG} > ${tmpfile}
	SELECT
		ad_bindpw

	FROM
		directoryservice_activedirectory

	ORDER BY
		-id

	LIMIT 1;
__SQL__

	exec 3<&0
	exec 0<"${tmpfile}"
	while read -r line
	do
		local var="$(echo ${line}|cut -f1 -d=|sed -Ee 's#^[[:space:]]+##' -e 's#[[:space:]]+$##')"
		local val="$(echo ${line}|cut -f2- -d=|sed -Ee 's#^[[:space:]]+##' -e 's#[[:space:]]+$##')"

		AD_set "${var}" "$(/usr/local/bin/midclt call notifier.pwenc_decrypt ${val})"
	done
	exec 0<&3
	rm "${tmpfile}"
}

AD_init()
{
	if ! AD_load_values
	then
		return 1
	fi

	AD_load_bindpw
	return 0
}

activedirectory_enabled()
{
	enabled="$(${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "\
	SELECT
		ad_enable
	FROM
		directoryservice_activedirectory
	ORDER BY
		-id
	LIMIT 1")"

	if [ "${enabled}" = "1" ]
	then
		return 0
	elif [ "${enabled}"  = "0" ]
	then
		return 1
	fi

	return 2
}

activedirectory_set()
{
	local enable="${1}"

	if [ -z "${enable}" ]
	then
		return 0
	fi

	${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "\
	UPDATE
		directoryservice_activedirectory
	SET
		ad_enable = ${enable}
	"

	return $?
}
